package com.djakapm.swing.common;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class IconManager {
    public static boolean VERBOSE = false;
    private Map<String, IconModel> iconCache = new HashMap<String,IconModel>();
    private static final String[] supportedFileExtension = {"png","jpg","gif"}; 
    public IconModel getIcon(String iconName){
        if(!this.iconCache.containsKey(iconName)){throw new RuntimeException("Cannot find icon name: "+iconName);}
        return this.iconCache.get(iconName);
    }

    public IconManager removeIcons(String... iconNames){
        if(iconNames == null){return this;}
        for(String iconName : iconNames){
            this.removeIcon(iconName);
        }
        return this;
    }

    public IconManager removeIcon(String iconName){
        this.iconCache.remove(iconName);
        return this;
    }

    @SuppressWarnings("unchecked")
    private void addIconFromDirectory(String path) throws IOException{
        File directory = new File(path);
        Collection<File> files = FileUtils.listFiles(directory,supportedFileExtension,true);
        if(files.isEmpty()){throw new IOException("Cannot found any icon files in "+path+", supported file types are PNG, JPG and GIF format");}
        for(File file : files){
            this.addIcon(file.getPath());
        }
    }
    
    private void addIconFromURL(String url) throws IOException {
        IconModel iconModel;
        String iconName;
        iconName = FilenameUtils.getName(url);
        if(this.existInCache(iconName)){return;}
        iconModel = this.createIconFromImageURL(url);
        this.iconCache.put(iconName,iconModel);
    }


    private void addIconFromFile(String path) throws IOException {
        IconModel iconModel;
        String iconName;
        iconName = FilenameUtils.getName(path);
        if(this.existInCache(iconName)){return;}
        iconModel = this.createIconFromImageFile(path);
        this.iconCache.put(iconName,iconModel);
    }

    private void addIconFromURL(String path,int iconWidth, int iconHeight, int hGap, int vGap) throws IOException {
        IconModel iconModel;
        String iconName;
        iconName = FilenameUtils.getName(path);
        if(this.existInCache(iconName)){return;}
        iconModel = this.createIconFromImageURL(path)
        .setIconWidth(iconWidth)
        .setIconHeight(iconHeight)
        .sethGap(hGap)
        .setvGap(vGap);

        this.iconCache.put(iconName,iconModel);
    }

    private void addIconFromFile(String path, int iconWidth, int iconHeight, int hGap, int vGap) throws IOException {
        IconModel iconModel;
        String iconName;
        iconName = FilenameUtils.getName(path);
        if(this.existInCache(iconName)){return;}
        iconModel = this.createIconFromImageFile(path)
        .setIconWidth(iconWidth)
        .setIconHeight(iconHeight)
        .sethGap(hGap)
        .setvGap(vGap);

        this.iconCache.put(iconName,iconModel);
    }

    public IconManager addIcons(String... paths) throws IOException{
        if(paths == null){throw new IOException("Cannot create icons, because the string array of paths is null");}
        for(String path : paths){
            this.addIcon(path);
        }
        return this;
    }


    public IconManager addIconSprite(String path, int iconWidth, int iconHeight, int hGap, int vGap) throws IOException {
        if(StringUtils.isBlank(path)){throw new IOException("Cannot create icon, because the file path is blank.");}

        if(path.startsWith("http")){
            this.addIconFromURL(path,iconWidth,iconHeight,hGap,vGap);
        }
        else{
            this.addIconFromFile(path,iconWidth,iconHeight,hGap,vGap);
        }
        return this;
    }

    public IconManager addIcon(String path) throws IOException {
        if(StringUtils.isBlank(path)){throw new IOException("Cannot create icon, because the file path is blank.");}
        if(VERBOSE){
            System.out.println("Current working directory: "+System.getProperty("user.dir"));
            System.out.println("Adding to icon cache from "+path+".");
        }
        
        if(this.directoryPath(path)){
            this.addIconFromDirectory(path);
        }
        else{
            if(path.startsWith("http")){
                this.addIconFromURL(path);
            }
            else{
                this.addIconFromFile(path);
            }
        }
        return this;
    }

    private boolean directoryPath(String path){
        return "".equals(FilenameUtils.getExtension(path));
    }

    public int cacheSize(){
        return this.iconCache.size();
    }
    private IconModel createIconFromImageURL(String url) throws IOException{
        URL urlObj = new URL(url);
        URLConnection connection = urlObj.openConnection();
        connection.connect();
        InputStream is = connection.getInputStream();
        byte[] bytes = IOUtils.toByteArray(is);
        if(bytes.length == 0){
            if(is != null){is.close();}
            throw new IOException("Cannot create icon, file found but it is empty(zero sized).");
        }
        InputStream imageInputStream = new ByteArrayInputStream(bytes);
        BufferedImage bi = ImageIO.read(imageInputStream);
        imageInputStream.close();

        Icon icon = new ImageIcon(bytes);

        return new IconModel(bi,icon);
    }

    private IconModel createIconFromImageFile(String path) throws IOException{
        File imageFile = new File(path);
        return this.createIconFromImageFile(imageFile);
    }

    private IconModel createIconFromImageFile(File imageFile) throws IOException {
        byte[] bytes = FileUtils.readFileToByteArray(imageFile);
        if(bytes.length == 0){throw new IOException("Cannot create icon, file found but it is empty(zero sized).");}
        return new IconModel(ImageIO.read(imageFile),new ImageIcon(bytes));
    }

    private boolean existInCache(String path){
        return this.iconCache.containsKey(path);
    }

    public Icon getSpriteFromIcon(String path, int row, int col){
        int x,y,iconWidth,iconHeight;
        int hGap, vGap;

        IconModel iconModel = this.iconCache.get(path);
        BufferedImage image = iconModel.getIconImage();
        ImageIcon spriteIcon = new ImageIcon();
        BufferedImage spriteImage;

        iconWidth = iconModel.getIconWidth();
        iconHeight = iconModel.getIconHeight();
        hGap = iconModel.gethGap();
        vGap = iconModel.getvGap();

        x = col * iconWidth + col * hGap;
        y = row * iconHeight + row * vGap;

        spriteImage = image.getSubimage(x,y,iconWidth,iconHeight);
        spriteIcon.setImage(spriteImage);
        return spriteIcon;
    }

}
